package rfcomm;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

public class Cliente
    implements
        DiscoveryListener
{
    private Object lock;
    private Vector<RemoteDevice> dDescobertos;
    private String connectionURL;
    private LocalDevice localDevice;
    private DiscoveryAgent dAgente;
    private UUID[] uuidset;
    private Envelope envelope;
    private Status status;

    public Cliente(
        String uuidgen,
        Envelope envelope,
        Status status )
    {
        lock = new Object();
        dDescobertos = new Vector<RemoteDevice>();
        uuidset = new UUID[ 1 ];
        uuidset[ 0 ] = new UUID( uuidgen, false );
        try {
            localDevice = LocalDevice.getLocalDevice();
        } catch( BluetoothStateException e ) {
            System.out.println( "Cliente: Não foi possivel pegar o dispositivo local" );
        }
        this.envelope = envelope;
        this.status = status;
    }

    public Vector<RemoteDevice> listDevicesFounded()
    {
        dAgente = localDevice.getDiscoveryAgent();
        System.out.println( "Inquiry..." );
        try {
            dAgente.startInquiry( DiscoveryAgent.GIAC, this );
        } catch( BluetoothStateException e ) {
            System.out.println( "Cliente: Inquiry mal sucedida." );
            e.printStackTrace();
        }

        try {
            synchronized( lock ) {
                lock.wait();
            }
        } catch( InterruptedException e ) {
            e.printStackTrace();
        }
        System.out.println( "Cliente: Inquiry completo." );

        return dDescobertos;
    }

    public StreamConnection connectToDevice(
        RemoteDevice rd )
    {
        StreamConnection sc = null;
        List<PairNeighborConnection> pairs;
        pairs = envelope.getPaired();

        for( PairNeighborConnection pairNeighborConnection : pairs ) {
            if( rd.getBluetoothAddress().equals(
                pairNeighborConnection.getNeighbor().getBluetoothAddress() ) )
                return pairNeighborConnection.getSc();
        }

        try {
            System.out.println( "Cliente: Buscando serviços..." );
            dAgente.searchServices( null, uuidset, rd, this );
        } catch( BluetoothStateException e ) {
            System.out.println( "Cliente: Não foi possivel buscar serviços." );
            e.printStackTrace();
        }
        try {
            synchronized( lock ) {
                lock.wait();
            }
        } catch( InterruptedException e ) {
            e.printStackTrace();
        }

        if( connectionURL != null ) {
            try {
                System.out.println( connectionURL );
                sc = (StreamConnection) Connector.open( connectionURL );
                envelope.setPaired( new PairNeighborConnection( rd, sc ) );
            } catch( IOException e ) {
                System.out.println( "Cliente: problema ao conectar com o servidor." );
                e.printStackTrace();
            }
        }
        return sc;
    }

    public RemoteDevice getDeviceByIndex(
        int index )
    {
        List<PairNeighborConnection> pairs;
        RemoteDevice rd;
        rd = dDescobertos.elementAt( index - 1 );
        pairs = envelope.getPaired();

        for( PairNeighborConnection pairNeighborConnection : pairs ) {
            if( rd.getBluetoothAddress().equals(
                pairNeighborConnection.getNeighbor().getBluetoothAddress() ) )
                System.out.println( "Dispositivo já conectado " + rd.getBluetoothAddress() );
        }
        return ( rd );
    }

    /**
     * @param conn hub of connection to the specific device Exchange the
     *        message.
     */
    public void communication(
        final BufferedReader bufr,
        final PrintWriter printwriter )
    {

        Thread CommunicationProcess = new Thread() {
            public void run()
            {
                int ping = 0;
                while( ping < 4 ) {
                    if( ! envelope.getToken().equals( "P" ) ) { /*
                                                                 * When it
                                                                 * hasn't token
                                                                 * then write
                                                                 */
                        System.out.println( "Nao estou com token" );
                        try {
                            while( ! bufr.ready() )
                                try {
                                    Thread.sleep( 100 );
                                } catch( InterruptedException e ) {
                                    e.printStackTrace();
                                }
                            envelope.setToken( bufr.readLine().trim() );
                        } catch( IOException e ) {
                            e.printStackTrace();
                        }
                    } else { /* When it has token then write */
                        System.out.println( "Estou com token" );
                        printwriter.write( envelope.getToken() );
                        printwriter.write( "\n" );
                        printwriter.flush();
                        envelope.setToken( " " );
                        ping++;
                    }
                }
                status.setStatus( false );
            }
        };

        CommunicationProcess.start();
    }

    /**
     * get list of paired devices and stream connection
     * 
     * @return list
     */
    public List<PairNeighborConnection> getListOfConnectedDevices()
    {
        return envelope.getPaired();
    }

    @Override
    public void deviceDiscovered(
        RemoteDevice rd,
        DeviceClass dc )
    {
        if( ! dDescobertos.contains( rd ) )
            dDescobertos.add( rd );
    }

    @Override
    public void inquiryCompleted(
        int arg0 )
    {
        synchronized( lock ) {
            lock.notify();
        }
    }

    @Override
    public void serviceSearchCompleted(
        int arg0,
        int arg1 )
    {
        synchronized( lock ) {
            lock.notify();
        }
    }

    @Override
    public void servicesDiscovered(
        int arg0,
        ServiceRecord[] srec )
    {
        if( srec != null && srec.length > 0 )
            connectionURL = srec[ 0 ].getConnectionURL( srec[ 0 ].NOAUTHENTICATE_NOENCRYPT, false );
        synchronized( lock ) {
            lock.notify();
        }
    }
}